package com.soyotec.algorithm.util.file;

import cn.hutool.core.io.file.FileWriter;
import cn.hutool.json.JSONUtil;
import com.soyotec.algorithm.solution.ValueWrapSolution;
import com.soyotec.algorithm.status.ExecutionStatus;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instances;
import weka.core.converters.ArffSaver;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 文件编辑器
 */
public class FileEditor {

    private FileResolver fileResolver;

    private List<String> typeList;

    public FileEditor() {
    }

    public FileEditor(FileResolver fileResolver) {
        this.fileResolver = fileResolver;
        init();
    }

    public void init() {
        typeList = new ArrayList<>();
        typeList.add("Integer");
        typeList.add("float");
    }

    /**
     * 创建设计变量文件
     *
     * @param path
     * @return
     */
    public File createVariableFile(String path, ValueWrapSolution solution) {
        File file = new File(path + File.separator + FileConstant.VARIABLE_FILE_NAME);
        try {
            file.createNewFile();
            //1 创建Attribute
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("index"));
            attributes.add(new Attribute("type", typeList));
            attributes.add(new Attribute("lower"));
            attributes.add(new Attribute("upper"));
            attributes.add(new Attribute("value"));

            // 2. create Instances object
            Instances instances = new Instances(FileConstant.VARIABLE_FILE_NAME, attributes, 0);

            //3填充data域
            int variableLength = solution.getNumberOfVariables();
            for (int i = 0; i < variableLength; i++) {
                double[] vals = new double[instances.numAttributes()];
                vals[0] = i;
                vals[1] = typeList.indexOf("float");
                vals[2] = solution.getVariableLimit(i).getLowerBound().getDoubleValue();
                vals[3] = solution.getVariableLimit(i).getUpperBound().getDoubleValue();
                vals[4] = solution.getVariableValue(i).getValue().getDoubleValue();
                instances.add(new DenseInstance(1.0, vals));
            }
            //4写入文件
            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建约束条件文件
     *
     * @param path
     * @return
     */
    public File createConstraintFile(String path, ValueWrapSolution solution) {
        File file = new File(path + File.separator + FileConstant.CONSTRAINT_FILE_NAME);
        try {
            file.createNewFile();
            //1 创建Attribute
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("index"));
            attributes.add(new Attribute("type", typeList));
            attributes.add(new Attribute("left"));
            attributes.add(new Attribute("left_operator", (ArrayList<String>) null));
            attributes.add(new Attribute("right"));
            attributes.add(new Attribute("right_operator", (ArrayList<String>) null));

            // 2. create Instances object
            Instances instances = new Instances(FileConstant.CONSTRAINT_FILE_NAME, attributes, 0);

            //3填充data域
            int constraintLength = solution.getNumberOfConstraints();
            for (int i = 0; i < constraintLength; i++) {
                double[] vals = new double[instances.numAttributes()];
                vals[0] = i;
                vals[1] = typeList.indexOf("float");
                vals[2] = solution.getConstraintLimit(i).getLowerBound().getDoubleValue();
                vals[3] = instances.attribute(3).addStringValue("<=");
                vals[4] = solution.getConstraintLimit(i).getUpperBound().getDoubleValue();
                vals[5] = instances.attribute(5).addStringValue("<=");
                instances.add(new DenseInstance(1.0, vals));
            }

            //4写入文件
            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建目标函数文件
     *
     * @param path
     * @return
     */
    public File createObjectiveFile(String path, ValueWrapSolution solution) {
        File file = new File(path + File.separator + FileConstant.OBJECTIVE_FILE_NAME);
        try {
            file.createNewFile();
            //1 创建Attribute
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("index"));
            attributes.add(new Attribute("type", typeList));

            // 2. create Instances object
            Instances instances = new Instances(FileConstant.OBJECTIVE_FILE_NAME, attributes, 0);

            //3填充data域
            int objectiveLength = solution.getNumberOfObjectives();
            for (int i = 0; i < objectiveLength; i++) {
                double[] vals = new double[instances.numAttributes()];
                vals[0] = i;
                vals[1] = typeList.indexOf("float");
                instances.add(new DenseInstance(1.0, vals));
            }
            //4写入文件
            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建算法参数文件
     *
     * @param path
     * @return
     */
    public File createAlgorithmParameterFile(String path, Map<String, Object> parameter) {
        File file = new File(path + File.separator + FileConstant.ALGORITHM_PARAMETER);
        try {
            file.createNewFile();
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("key", (ArrayList<String>) null));
            attributes.add(new Attribute("value", (ArrayList<String>) null));

            Instances instances = new Instances(FileConstant.ALGORITHM_PARAMETER, attributes, 0);

            if (parameter != null && !parameter.isEmpty()) {
                for (Map.Entry<String, Object> entry : parameter.entrySet()) {
                    double[] vals = new double[instances.numAttributes()];
                    vals[0] = instances.attribute(0).addStringValue(entry.getKey());
                    vals[1] = instances.attribute(1).addStringValue(entry.getValue().toString());
                    instances.add(new DenseInstance(1.0, vals));
                }
            }
            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建运行时设计变量文件
     *
     * @param path
     * @return
     */
    public File createRuntimeVariableFile(String path, ValueWrapSolution doubleSolution) {
        File file = new File(path + File.separator + FileConstant.RUNTIME_VARIABLE_FILE_NAME);
        try {
            file.createNewFile();
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("no"));
            int variableLength = doubleSolution.getNumberOfVariables();
            for (int i = 0; i < variableLength; i++) {
                attributes.add(new Attribute("var" + i));
            }

            Instances instances = new Instances(FileConstant.RUNTIME_VARIABLE_FILE_NAME, attributes, 0);

            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建运行时约束条件文件
     *
     * @param path
     * @return
     */
    public File createRuntimeConstraintFile(String path, ValueWrapSolution doubleSolution) {
        File file = new File(path + File.separator + FileConstant.RUNTIME_CONSTRAINT_FILE_NAME);
        try {
            file.createNewFile();
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("no"));
            int constraintLength = doubleSolution.getNumberOfConstraints();
            for (int i = 0; i < constraintLength; i++) {
                attributes.add(new Attribute("con" + i + "_distance"));  //第一个是距离
                attributes.add(new Attribute("con" + i + "_value"));   //第二个是真实值
            }
            Instances instances = new Instances(FileConstant.RUNTIME_CONSTRAINT_FILE_NAME, attributes, 0);

            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建运行时目标函数文件
     *
     * @param path
     * @return
     */
    public File createRuntimeObjectiveFile(String path, ValueWrapSolution doubleSolution) {
        File file = new File(path + File.separator + FileConstant.RUNTIME_OBJECTIVE_FILE_NAME);
        try {
            file.createNewFile();
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("no"));
            int objectiveLength = doubleSolution.getNumberOfObjectives();
            for (int i = 0; i < objectiveLength; i++) {
                attributes.add(new Attribute("obj" + i));
            }
            Instances instances = new Instances(FileConstant.RUNTIME_OBJECTIVE_FILE_NAME, attributes, 0);

            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建结果文件
     *
     * @param path
     * @return
     */
    public File createResultFile(String path, ValueWrapSolution doubleSolution) {
        File file = new File(path + File.separator + FileConstant.RESULT_FILE_NAME);
        try {
            file.createNewFile();
            ArrayList<Attribute> attributes = new ArrayList<>();
            attributes.add(new Attribute("no"));
            int variableLength = doubleSolution.getNumberOfVariables();
            int constraintLength = doubleSolution.getNumberOfConstraints();
            int objectiveLength = doubleSolution.getNumberOfObjectives();

            for (int i = 0; i < variableLength; i++) {
                attributes.add(new Attribute("var" + i));
            }
            for (int i = 0; i < constraintLength; i++) {
                attributes.add(new Attribute("con" + i));
            }
            for (int i = 0; i < objectiveLength; i++) {
                attributes.add(new Attribute("obj" + i));
            }

            Instances instances = new Instances(FileConstant.RESULT_FILE_NAME, attributes, 0);

            writeArffFileContent(file, instances);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 创建运行状态文件
     *
     * @param path
     * @return
     */
    public File createExecutionStatusFile(String path) {
        File file = new File(path + File.separator + FileConstant.EXECUTION_STATUS_FILE_NAME);
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 写ARFF文件
     *
     * @param file
     * @param instances
     */
    public void writeArffFileContent(File file, Instances instances) throws IOException {
        ArffSaver saver = new ArffSaver();
        saver.setMaxDecimalPlaces(20);
        saver.setInstances(instances);
        saver.setFile(file);
        saver.writeBatch();
    }

    public void updateRuntimeConstraintFile(String path, List<ValueWrapSolution> doubleSolutionList) {
        try {
            if (doubleSolutionList != null && !doubleSolutionList.isEmpty()) {
                File file = new File(path + File.separator + FileConstant.RUNTIME_CONSTRAINT_FILE_NAME);
                ArrayList<Attribute> attributes = new ArrayList<>();
                attributes.add(new Attribute("no"));
                ValueWrapSolution doubleSolution = doubleSolutionList.get(0);
                int constraintLength = doubleSolution.getNumberOfConstraints();
                for (int i = 0; i < constraintLength; i++) {
                    attributes.add(new Attribute("con" + i + "_distance"));  //第一个是距离
                    attributes.add(new Attribute("con" + i + "_value"));   //第二个是真实值
                }

                Instances instances = new Instances(FileConstant.RUNTIME_CONSTRAINT_FILE_NAME, attributes, 0);

                for (int i = 0; i < doubleSolutionList.size(); i++) {
                    ValueWrapSolution doubleSolution1 = doubleSolutionList.get(i);
                    double[] vals = new double[instances.numAttributes()];
                    vals[0] = i;
                    for (int j = 0; j < constraintLength * 2; j+=2) {
                        vals[j + 1] = doubleSolution1.getConstraint(j / 2).getValue().getDoubleValue();
                        vals[j + 2] = doubleSolution1.getConstraintRealValue(j / 2).getValue().getDoubleValue();
//                        System.out.println("写入的约束距离是：" + vals[j + 1]);
//                        System.out.println("写入的约束值是：" + vals[j + 2]);
                    }
                    instances.add(new DenseInstance(1.0, vals));
                }
                writeArffFileContent(file, instances);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        double x1 = 1.0D;
        double x2 = 4.743049222385663D;
        double x3 = 3.8210852015617105D;
        double x4 = 1.3794172568996474D;

        BigDecimal bigDecimal1 = new BigDecimal("1");
        BigDecimal bigDecimal2 = new BigDecimal("4.743049222385663");
        BigDecimal bigDecimal3 = new BigDecimal("3.8210852015617105");
        BigDecimal bigDecimal4 = new BigDecimal("1.3794172568996474");
        System.out.println("big:" + bigDecimal1.multiply(bigDecimal2).multiply(bigDecimal3).multiply(bigDecimal4).doubleValue());

        double y=x1*x4*(x1+x2+x3)+x3;
        double g1=x1*x2*x3*x4;
        double g2=x1*x1+x2*x2+x3*x3+x4*x4;

//        y=17.014017273262684;
//        g1=24.999999967579708;
//        g2=40.000000012199685;

        System.out.println("y:" + y);
        System.out.println("g1:" + g1);
        System.out.println("g2:" + g2);

    }

    public void updateRuntimeObjectiveFile(String path, List<ValueWrapSolution> doubleSolutionList) {
        try {
            if (doubleSolutionList != null && !doubleSolutionList.isEmpty()) {
                File file = new File(path + File.separator + FileConstant.RUNTIME_OBJECTIVE_FILE_NAME);
                ArrayList<Attribute> attributes = new ArrayList<>();
                attributes.add(new Attribute("no"));
                ValueWrapSolution doubleSolution = doubleSolutionList.get(0);
                //todo 这里暂时先写死 为了给jack交付客户使用 不然修改的地方较多 稳妥起见 且现在只有sqpdfo还是个单目标的
//                int ObjectiveLength = doubleSolution.getNumberOfObjectives();
                int ObjectiveLength = 1;
                for (int i = 0; i < ObjectiveLength; i++) {
                    attributes.add(new Attribute("obj" + i));
                }

                Instances instances = new Instances(FileConstant.RUNTIME_OBJECTIVE_FILE_NAME, attributes, 0);
                for (int i = 0; i < doubleSolutionList.size(); i++) {
                    ValueWrapSolution doubleSolution1 = doubleSolutionList.get(i);
                    double[] vals = new double[instances.numAttributes()];
                    vals[0] = i;
                    for (int j = 0; j < ObjectiveLength; j++) {
                        vals[j + 1] = doubleSolution1.getObjective(j).getValue().getDoubleValue();
                        System.out.println("写入的目标是：" + vals[j + 1]);
                    }
                    instances.add(new DenseInstance(1.0, vals));
                }
                writeArffFileContent(file, instances);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void updateExecutionStatus(String path, ExecutionStatus executionStatus) {
        if (executionStatus != null) {
            String jsonStr = JSONUtil.toJsonStr(executionStatus);
            File file = new File(path + File.separator + FileConstant.EXECUTION_STATUS_FILE_NAME);
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.append(jsonStr + "\n");
            fileResolver.executionStatusIndexIncrease(1);
        }
    }
}
